#include "network/tcp_server.hpp"

#include "base/assert.hpp"
#include "base/log.hpp"
#include "event/cabinet.hpp"
#include "network/tcp_acceptor.hpp"
#include "network/tcp_connection.hpp"

#include <limits>

namespace ibox::network {

using namespace std::placeholders;
using namespace ibox::event;

// 私有数据
struct TcpServer::Data {
    event::Loop *m_loop = nullptr;

    ConnectedCallback m_connected_cb;
    DisconnectedCallback m_disconnected_cb;
    ReceiveCallback m_receive_cb;
    size_t m_receive_threshold = 0;

    std::unique_ptr<TcpAcceptor> m_acceptor{};
    Cabinet<TcpConnection> conns;

    State m_state = State::None;
};

TcpServer::TcpServer(event::Loop *m_loop) : m_data(std::make_unique<Data>()) {
    expect(m_data != nullptr);

    m_data->m_loop = m_loop;
    m_data->m_acceptor = std::make_unique<TcpAcceptor>(m_loop);
}

TcpServer::~TcpServer() { cleanup(); }

bool TcpServer::initialize(const SockAddr &bind_addr, int listen_backlog) {
    if (m_data->m_state != State::None) {
        return false;
    }

    if (m_data->m_acceptor->initialize(bind_addr, listen_backlog)) {
        m_data->m_acceptor->set_new_connection_callback(
            [&](TcpConnection *conn) { on_tcp_connected(conn); });
        m_data->m_state = State::Inited;
        return true;
    }

    return false;
}

void TcpServer::set_connected_callback(ConnectedCallback cb) {
    m_data->m_connected_cb = std::move(cb);
}

void TcpServer::set_disconnected_callback(DisconnectedCallback cb) {
    m_data->m_disconnected_cb = std::move(cb);
}

void TcpServer::set_receive_callback(ReceiveCallback cb, size_t threshold) {
    m_data->m_receive_cb = std::move(cb);
    m_data->m_receive_threshold = threshold;
}

bool TcpServer::start() {
    if (m_data->m_state != State::Inited) {
        return false;
    }

    if (m_data->m_acceptor->start()) {
        m_data->m_state = State::Running;
        return true;
    }
    return false;
}

void TcpServer::stop() {
    LOG_DEBUG("stopping tcp server ...");
    if (m_data->m_state != State::Running) {
        return;
    }

    m_data->conns.foreach ([](TcpConnection *conn) {
        conn->disconnect();
        delete conn;
    });
    m_data->conns.clear();

    m_data->m_acceptor->stop();
    m_data->m_state = State::Inited;
}

void TcpServer::cleanup() {
    LOG_DEBUG("cleaning up tcp server ...");
    if (m_data->m_state <= State::None) {
        return;
    }

    stop();

    m_data->m_acceptor->cleanup();

    m_data->m_connected_cb = nullptr;
    m_data->m_disconnected_cb = nullptr;
    m_data->m_receive_cb = nullptr;
    m_data->m_receive_threshold = 0;

    m_data->m_state = State::None;
}

bool TcpServer::send(const ConnToken *conn,
                     const void *data_ptr,
                     size_t data_size) {
    auto *tcp_conn = m_data->conns.at(conn);
    if (tcp_conn != nullptr) {
        return tcp_conn->send(data_ptr, data_size);
    }
    return false;
}

bool TcpServer::disconnect(const ConnToken *conn) {
    auto *tcp_conn = m_data->conns.free(conn);
    if (tcp_conn != nullptr) {
        tcp_conn->disconnect();
        return true;
    }
    return false;
}

bool TcpServer::shutdown(const ConnToken *conn, int howto) {
    auto *tcp_conn = m_data->conns.at(conn);
    if (tcp_conn != nullptr) {
        return tcp_conn->shutdown(howto);
    }
    return false;
}

bool TcpServer::is_client_valid(const ConnToken *conn) const {
    return m_data->conns.at(conn) != nullptr;
}

const SockAddr *TcpServer::get_client_address(const ConnToken *conn) const {
    auto *tcp_conn = m_data->conns.at(conn);
    if (tcp_conn != nullptr) {
        return tcp_conn->peer_addr();
    }
    return nullptr;
}

void *TcpServer::set_context(const ConnToken *conn, void *context) {
    auto *tcp_conn = m_data->conns.at(conn);
    if (tcp_conn != nullptr) {
        return tcp_conn->set_context(context);
    }
    return nullptr;
}

void *TcpServer::get_context(const ConnToken *conn) const {
    auto *tcp_conn = m_data->conns.at(conn);
    if (tcp_conn != nullptr) {
        return tcp_conn->get_context();
    }
    return nullptr;
}

TcpServer::State TcpServer::state() const { return m_data->m_state; }

void TcpServer::on_tcp_connected(TcpConnection *new_conn) {
    LOG_DEBUG("tcp connected");
    // 指针保存到Cabinet中
    // 在free返回后, 需要手动delete
    ConnToken conn = m_data->conns.save(new_conn);
    // 需要复制ConnToken(conn)
    new_conn->set_receive_callback(
        // 注意conn使用值传递方式
        [&, conn](Buffer &buf) { on_tcp_received(&conn, buf); },
        m_data->m_receive_threshold);
    new_conn->set_disconnected_callback(
        // 注意conn使用值传递方式
        [&, conn]() { on_tcp_disconnected(&conn); });

    if (m_data->m_connected_cb) {
        m_data->m_connected_cb(&conn);
    }
}

void TcpServer::on_tcp_disconnected(const ConnToken *conn) {
    LOG_DEBUG("tcp disconnected");
    if (m_data->m_disconnected_cb) {
        m_data->m_disconnected_cb(conn);
    }

    TcpConnection *tcp_conn = m_data->conns.free(conn);
    LOG_DEBUG("delete tcp connection: {}", tcp_conn->peer_addr()->to_string());
    delete tcp_conn;
}

void TcpServer::on_tcp_received(const ConnToken *conn, Buffer &buff) {
    LOG_DEBUG("tcp received");
    if (m_data->m_receive_cb) {
        m_data->m_receive_cb(conn, buff);
    }
}

} // namespace ibox::network
